#! /bin/bash
########################################
#  This file is part of the open-eBackup project.
# Copyright (c) 2024 Huawei Technologies Co.,Ltd.
#
# open-eBackup is licensed under MPL v2.
# You can use this software according to the terms and conditions of the MPL v2.
# You may obtain a copy of MPL v2 at:
#
#          https://www.mozilla.org/en-US/MPL/2.0
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the MPL v2 for more details.
########################################

YSH_version='0.1.5'
YAML_AWK_PARSER='
function raise(msg) { print msg > "/dev/stderr"; if (force_complete) { exit_status = 1; } else { exit 1; };};
function level() { match($0, /^[[:space:]]*/); if (RLENGTH % 2 != 0) { raise("Bad indentation on line "NR". Number of spaces uneven."); }; return RLENGTH / 2;};
function join_stack(depth) { r = sprintf("%"block_level"s",""); gsub(/ /,"-",r); for (i = 0; i <= depth; i++) { r = r "." stack[i]; }; sub(/^\./, "", r); return r;};
function safe_split(input, output) { split(input, chars, ""); in_quote = 1; acc = ""; count = 0; for (i=0; i <= length(input); i++) { c=chars[i]; if (c=="\"") { in_quote = (in_quote + 1) % 2; }; if (c=="," && in_quote) { output[count++] = acc; acc = ""; } else { acc = acc c; }; }; output[count++] = acc;};
function remove_sur_quotes(target) { sub(/^[[:space:]]*\"/, "", target) ; sub(/\"[[:space:]]*$/, "", target) ; return target;};
function check_started() { if (started == 0) { raise("Keys must be added before line "NR); };};
/^---/ { if (started==0) { started=1; } else { block_level++; }; next;};
/^[[:space:]]*[^[:space:]]+:/ { started=1; depth=level(); key=$1; sub(/:.*$/, "", key); stack[depth] = key;};
/^[[:space:]]*[^[:space:]]+:[[:space:]]+[^[:space:]]+/ { started=1; depth=level(); val=$0; sub(/^[[:space:]]*[^[:space:]]+:[[:space:]]+/, "", val); val = remove_sur_quotes(val); print join_stack(depth) "=\"" val "\""; next;};
/^[[:space:]]*\-/ { check_started(); depth=level(); stack_key=join_stack(depth-1); indx=list_counter[stack_key]++; stack[depth]="[" indx "]";};
/^[[:space:]]*-[[:space:]]+\{.*\}[[:space:]]*$/ { check_started(); line=$0; sub(/^[[:space:]]*-[[:space:]]+\{/, "", line); sub(/\}[[:space:]]*$/, "", line); safe_split(line, entries); for (i in entries) { key=entries[i]; val=entries[i]; sub(/^[[:space:]]*/, "", key); sub(/:.*$/, "", key); sub(/^[[:space:]]*[^[:space:]]+:[[:space:]]+\"?/, "", val) ; sub(/\"?[[:space:]]*$/, "", val) ; val = remove_sur_quotes(val); print join_stack(depth) "." key "=\"" val "\""; }; delete entries; next;};
/^[[:space:]]*-[[:space:]][^[:space:]]+:/ { check_started(); depth++; key=$0; sub(/^[[:space:]]*-[[:space:]]/, "", key); sub(/:.*$/, "", key); stack[depth]=key;};
/^[[:space:]]*-[[:space:]][^[:space:]]+:[[:space:]]+[^[:space:]]+/ { check_started(); val=$0; sub(/^[[:space:]]*-[[:space:]][^[:space:]]+:[[:space:]]+/, "", val); val = remove_sur_quotes(val); print join_stack(depth) "=\"" val "\""; next;};
/^[[:space:]]*-[[:space:]]+[^[:space:]]+/ { check_started(); val=$0; sub(/^[[:space:]]*-[[:space:]]+/, "", val); sub(/[[:space:]]*$/, "", val); val = remove_sur_quotes(val); print join_stack(depth) "=\"" val "\""; next;};
/^[[:space:]]*[^[:space:]]+:[[:space:]]*$/ {next};
/^[[:space:]]*-[[:space:]]+[^[:space:]]+:[[:space:]]*$/ {next};
/^[[:space:]]*$/ { next };
0
'
YSH_parse() {
    awk "${YAML_AWK_PARSER}" "${1}"
}
YSH_parse_sdin() {
    awk "${YAML_AWK_PARSER}"
}
YSH_query() {
    grep -E "^${2}" <<< "${1}" | sed -E "s/^${2}[\\.=]?//"
}
YSH_safe_query() {
    grep -E "^${2}=\".*\"$" <<< "${1}" | sed -E "s/^${2}=\"//" | sed -E "s/\"$//"
}
YSH_sub() {
    grep -E "^${2}[^=]" <<< "${1}" | sed "s/^$2\\.//"
}
YSH_list() {
    YSH_sub "${1}" "${2}" | grep -E "^\\[[0-9]+\\]"
}
YSH_list_values() {
    YSH_sub "${1}" "${2}" | grep -E "^\\[[0-9]+\\]=" | sed "s/^\\[[0-9]\\]=//" | sed "s/[(^\")(\"$)]//g"
}
YSH_count() {
    YSH_sub "${1}" "${2}" | grep -oE "^\\[[0-9]+\\]" | uniq | wc -l
}
YSH_index() {
    YSH_query "${1}" "\\[${2}\\]"
}
YSH_safe_index() {
    YSH_safe_query "${1}" "\\[${2}\\]"
}
YSH_safe_index() {
    YSH_safe_query "${1}" "\\[${2}\\]"
}
YSH_tops() {
    sed -E "s/[\\[\\.=].*$//" <<< "${1}" | uniq
}
YSH_next_block() {
    grep -E "^-.*" <<< "${1}" | sed -E "s/^-\\.?//g"
}
YSH_usage() {
    echo ""
    echo "Usage: ysh [-fT input] [queries]"
    echo ""
    echo "input:"
    echo "  -f, --file        <file_name>    parse file"
    echo "  -T, --transpiled  <file_name>    use pre-transpiled file"
    echo ""
    echo "queries:"
    echo "  -q, --query       <query>        generic query"
    echo "  -Q, --query-val   <query>        safe query. Guarentees a value."
    echo "  -s, --sub         <query>        sub structure. No values."
    echo "  -l, --list        <query>        query for a list"
    echo "  -c, --count       <query>        count length of list element"
    echo "  -i, --index       <i>            array access by index"
    echo "  -I, --index-val   <i>            safe array access by index. Guarentees a value."
    echo "  -t, --tops                       top level children keys of structure"
    echo "  -n, --next                       move to next block"
    echo "  -h, --help                       prints this message"
    echo ""
    echo "And even more at https://docs.yaml.sh"
}
ysh() {
    local YSH_RAW_STRING=""
    case "$1" in
    -v|--version)
        echo "v${YSH_version}" && exit 0
    ;;
    -h|--help)
        YSH_usage
    ;;
    -f|--file)
        YSH_RAW_STRING="$(YSH_parse "${2}")"
        shift; shift
    ;;
    -T|--transpiled)
        YSH_RAW_STRING="${2}"
        shift; shift
    ;;
    *)
        YSH_RAW_STRING="$(YSH_parse_sdin)"
    ;;
    esac
    while [ $# -gt 0 ] ; do
        case "$1" in
        -q|--query)
            YSH_RAW_STRING="$(YSH_query "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -Q|--query-val)
            YSH_RAW_STRING="$(YSH_safe_query "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -c|--count)
            YSH_RAW_STRING="$(YSH_count "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -l|--list)
            YSH_RAW_STRING="$(YSH_list "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -L|--list-val)
            YSH_RAW_STRING="$(YSH_list_values "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -i|--index)
            YSH_RAW_STRING="$(YSH_index "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -I|--index-val)
            YSH_RAW_STRING="$(YSH_safe_index "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -s|--sub)
            YSH_RAW_STRING="$(YSH_sub "${YSH_RAW_STRING}" "${2}")"
            shift
        ;;
        -n|--next)
            YSH_RAW_STRING="$(YSH_next_block "${YSH_RAW_STRING}")"
        ;;
        -t|--tops)
            YSH_RAW_STRING="$(YSH_tops "${YSH_RAW_STRING}")"
        ;;
        -*)
            echo "Unknown option: ${1} Refer to -h for help."
            exit 1
        ;;
        *)
            echo "Unknown Ussage!" > /dev/stdout
            exit 1
        ;;
        esac
        shift
    done
    if [[ $# -eq 0 ]]; then echo "${YSH_RAW_STRING}"; fi
}
if [[ YSH_LIB -ne 1 ]]; then
    if [[ $# -eq 0 ]] ; then YSH_usage; exit 1; fi
    ysh "$@";
fi
